package project2;


import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

public class ProxyServer {
	public static final String LOG_FILE = "log_proxyserver.txt";
	private static List<SocketThread> activeThread;
	private static final int CLEANING_INTERVAL = 1000;
	private static Integer request_num;
	private static Integer client_num;
	private static boolean ready;
	
	public static void main(String[] args) {
		ready = false;
		if (args.length != 2) {
			System.err.println("Usage: java ProxyServer <port> <type>");
			System.err.println("type: '-n' print messages to std out. Print errors to log.");
			System.err.println("	  '-a' print both messages and errors to std out. ");
			System.err.println("	  '-s' print both messages and errors to log. ");
			System.exit(-1);
		}
		
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
		PrintWriter errorOut = null;
		PrintWriter messageOut = null;
		try {
			if (args[1].equals("-a")) {
				errorOut = new PrintWriter(System.out);
				messageOut = new PrintWriter(System.out);
			} else if (args[1].equals("-s")){
				errorOut = new PrintWriter(new BufferedWriter(new FileWriter(LOG_FILE, true)));
				messageOut = new PrintWriter(new BufferedWriter(new FileWriter(LOG_FILE, true)));
			} else {
				errorOut = new PrintWriter(new BufferedWriter(new FileWriter(LOG_FILE, true)));
				messageOut = new PrintWriter(System.out);
			}
		} catch (IOException e) {
		    System.err.println("[ERROR] Error when connecting to the log file.");
		}
		// Initialize the Util.
		new Util(format, errorOut, messageOut);
		request_num = 0;
		client_num = 0;
		
		ServerSocket serverSocket = null;
		int port = -1;
		try {
			port = Integer.parseInt(args[0]);
			serverSocket = new ServerSocket(port);
		} catch (NumberFormatException e) {
			Util.pm("Please give a reasonable port range.");
			return;
		} catch (Exception e) {
			e.printStackTrace();
			Util.pm("Error happens when binding the port " + port + ", please try other port.");
			return;
		} 
		
		activeThread = new ArrayList<SocketThread>();
		
		try {
			Util.pm("Start...");
			ready = true;
			while (true) {
				Socket clientSocket = serverSocket.accept();
				System.out.println("!!!!!!!!!New Client In");
				registerThread(clientSocket);
			}
		} catch (IOException e) {
			Util.pe(e, "Error happens when accepting new connection.");
		}
	}
	
	/**
	 * Start the given thread.
	 * Add a thread checking whether the given thread is reasonable to live longer.
	 * Kill the given thread if they are no longer need to live.
	 * 
	 * @param clientSocket
	 * @return
	 */

	private static void registerThread(Socket clientSocket) {
		final SocketThread registeredThread = new SocketThread(clientSocket, getNextClientNum());
		try {
			clientSocket.setSoTimeout(CLEANING_INTERVAL * 3);
		} catch (SocketException e1) {
			Util.pe(e1, "Socket set time out error.", clientSocket);
		}
		activeThread.add(registeredThread);
		registeredThread.start();
		new Thread() {
			
			@Override
			public void run() {
				while (!registeredThread.isTerminate()) {
					try {
						Thread.sleep(CLEANING_INTERVAL);
					} catch (InterruptedException e) {
						Util.pe(e, "Error when trying to hibernating the cleaning thread in ProxyServer.");
					}
					if (!registeredThread.isUseful()) {
						// registeredThread.terminate();
						activeThread.remove(registeredThread);
					}
				}
			}
		}.start();
	}
	
	protected static int getNextRequestNum() {
		synchronized (request_num) {
			request_num ++;
			return request_num;
		}
	}
	
	protected static int getNextClientNum() {
		synchronized (client_num) {
			client_num ++;
			return client_num;
		}
	}

	public static boolean getReaddy() {
		// TODO Auto-generated method stub
		return ready;
	}
}
